/**
 * 
 */
package com.arx.damages;

import com.arx.character.Player;
import com.arx.common.Common;
import com.arx.interactive.Interactive;
import com.arx.interactive.InteractiveObject;
import com.arx.item.Equipment;
import com.arx.script.Script;
import com.arx.time.Time;
import com.cob.main.Interface;
import com.dalonedrau.d20.Diceroller;
import com.dalonedrau.opengl.Vector3;
import com.dalonedrau.pool.PooledStringBuilder;
import com.dalonedrau.pool.StringBuilderPool;
import com.dalonedrau.util.GlobalConstants;

/**
 * @author Donald
 */
public final class Damages {
	/** the singleton instance. */
	private static Damages	instance;
	/**
	 * Gets the one and only instance of {@link Damages}.
	 * @return {@link Damages}
	 */
	public static Damages getInstance() {
		if (Damages.instance == null) {
			Damages.instance = new Damages();
		}
		return Damages.instance;
	}
	private long			Blood_Duration		= 0;
	private float			Blood_Pos			= 0.f;
	private DamageInfo[]	damages				= new DamageInfo[DamageGlobals.MAX_DAMAGES];
	private boolean			REFUSE_GAME_RETURN	= true;
	ScreenSplats[]			ssplat				= new ScreenSplats[DamageGlobals.MAX_SCREEN_SPLATS];
	/** Hidden constructor. */
	private Damages() {}
	public void ARX_DAMAGE_Reset_Blood_Info() {
		Blood_Pos = 0.f;
		Blood_Duration = 0;
	}
	/**
	 * Call to render blood after a hit.
	 */
	public void ARX_DAMAGE_Show_Hit_Blood() {

	}
	/**
	 * Computes the repair price of an object.
	 * @param torepair the {@link InteractiveObject} to be repaired
	 * @param blacksmith the {@link InteractiveObject} doing the repairs
	 * @return float
	 */
	public float ARX_DAMAGES_ComputeRepairPrice(
			final InteractiveObject torepair, final InteractiveObject blacksmith) {
		float price = -1f;
		// calculate repair price IF:
		// 1. both parameters are not null
		// 2. the object to be repaired is an item
		// 3. the object to be repaired can be repaired beyond 0
		// 4. the object to be repaired isn't already at max durability
		if (torepair != null && blacksmith != null
				&& torepair.hasIoFlag(Interactive.IO_ITEM)
				&& torepair.getMaxDurability() > 0
				&& torepair.getDurability() != torepair.getMaxDurability()) {

			float ratio = (torepair.getMaxDurability() - torepair
					.getDurability()) / torepair.getMaxDurability();
			price = torepair.getItemData().getPrice() * ratio;

			if (blacksmith.getShopMultiplier() != 0.f) {
				price *= blacksmith.getShopMultiplier();
			}

			if (price > 0.f && price < 1.f) {
				price = 1.f;
			}
		}
		return price;
	}
	void ARX_DAMAGES_DamageFIX(final InteractiveObject io, final float dmg,
			final int source, final long flags, final Vector3 pos) {
		if (io != null && io.getShow() != 0
				&& io.hasIoFlag(InteractiveGlobals.IO_FIX)
				&& !io.hasIoFlag(InteractiveGlobals.IO_INVULNERABILITY)
				&& io.getScript() != null && io.getScript().hasData()) {
			io.setDmg_sum(io.getDmg_sum() + dmg);
			if (Interactive.getInstance().validIONum(source)) {
				Script.getInstance().setEVENT_SENDER(
						Interactive.getInstance().getInteractiveObject(source));
			} else {
				Script.getInstance().setEVENT_SENDER(null);
			}
			if (Time.getInstance().getARXTime() > io.getOuch_time() + 500) {
				io.setOuch_time(Time.getInstance().ARXTimeUL());
				String tex = String.format("%5.2f", io.getDmg_sum());
				Script.getInstance().SendIOScriptEvent(io,
						ScriptConstants.SM_OUCH, tex, null);
				io.setDmg_sum(0f);
			}
			if (Diceroller.getInstance().ARXRnd() * 100.f > io.getDurability()) {
				io.setDurability(io.getDurability() - dmg
						* GlobalConstants.DIV2);
			}
			if (io.getDurability() <= 0f) {
				io.setDurability(0f);
				Script.getInstance().SendIOScriptEvent(io,
						ScriptConstants.SM_BREAK, "");
			} else {
				PooledStringBuilder dmm = StringBuilderPool.getInstance()
						.getStringBuilder();
				if (Interactive.getInstance().isPlayer(
						Script.getInstance().getEVENT_SENDER())) {
					if ((flags & 1) == 1) {
						dmm.append(String.format("%f", dmg));
						dmm.append(" SPELL");
					} else {
						byte type = Equipment.getInstance()
								.ARX_EQUIPMENT_GetPlayerWeaponType();
						switch (type) {
						case EquipmentGlobals.WEAPON_BARE:
							dmm.append(String.format("%f", dmg));
							dmm.append(" BARE");
							break;
						case EquipmentGlobals.WEAPON_DAGGER:
							dmm.append(String.format("%f", dmg));
							dmm.append(" DAGGER");
							break;
						case EquipmentGlobals.WEAPON_1H:
							dmm.append(String.format("%f", dmg));
							dmm.append(" 1H");
							break;
						case EquipmentGlobals.WEAPON_2H:
							dmm.append(String.format("%f", dmg));
							dmm.append(" 2H");
							break;
						case EquipmentGlobals.WEAPON_BOW:
							dmm.append(String.format("%f", dmg));
							dmm.append(" ARROW");
							break;
						default:
							dmm.append(String.format("%f", dmg));
							break;
						}
					}
				} else {
					dmm.append(String.format("%f", dmg));
				}
				Script.getInstance().SendIOScriptEvent(io,
						ScriptConstants.SM_HIT, dmm.toString());
				dmm.returnToPool();
			}
		}
	}
	public float ARX_DAMAGES_DamagePlayer(final float dmg, final long type,
			final int source, final Vector3 pos) {
		float val = 0;
		if (!Player.getInstance().getPlayer()
				.hasPlayerFlag(PlayerGlobals.PLAYERFLAGS_INVULNERABILITY)) {
			float life = Player.getInstance().getPlayer().getLife();
			float damagesdone = 0.f;
			if (life != 0) {
				if (dmg > life) {
					damagesdone = dmg;
				} else {
					damagesdone = life;
				}
				// player is interactive object 0
				float dmgsum = Interactive.getInstance()
						.getInteractiveObject(0).getDmg_sum();
				Interactive.getInstance().getInteractiveObject(0)
						.setDmg_sum(dmgsum + dmg);
				dmgsum = Interactive.getInstance().getInteractiveObject(0)
						.getDmg_sum();

				long ouch = Interactive.getInstance().getInteractiveObject(0)
						.getOuch_time();
				if (Time.getInstance().getARXTime() > ouch + 500) {
					InteractiveObject oes = Script.getInstance()
							.getEVENT_SENDER();

					if (Interactive.getInstance().validIONum(source)) {
						Script.getInstance().setEVENT_SENDER(
								Interactive.getInstance().getInteractiveObject(
										source));
					} else {
						Script.getInstance().setEVENT_SENDER(null);
					}

					Interactive.getInstance().getInteractiveObject(0)
							.setOuch_time(Time.getInstance().ARXTimeUL());
					String tex = String.format("%5.2f", dmgsum);
					Script.getInstance().SendIOScriptEvent(
							Interactive.getInstance().getInteractiveObject(0),
							ScriptConstants.SM_OUCH, tex, null);
					Script.getInstance().setEVENT_SENDER(oes);
					dmgsum = Interactive.getInstance().getInteractiveObject(0)
							.getDmg_sum();
					float maxlife = Player.getInstance().getPlayer()
							.getMaxlife();
					float power = dmgsum / maxlife * 220.f;
					// AddQuakeFX(power * 3.5f, 500 + power * 3, rnd() * 100.f +
					// power + 200, 0);
					Interactive.getInstance().getInteractiveObject(0)
							.setDmg_sum(0);
				}

				if (dmg > 0.f) {
					if (Interactive.getInstance().validIONum(source)) {
						InteractiveObject pio = null;
						InteractiveObject ioSource = Interactive.getInstance()
								.getInteractiveObject(source);
						if (ioSource.hasIoFlag(InteractiveGlobals.IO_NPC)) {
							if (ioSource.hasNpcData()) {
								pio = ioSource.getNpcdata().getWeapon();
							}
							if (pio != null
									&& (pio.getPoisonous() == 0 || pio
											.getPoisonous_count() == 0)) {
								pio = null;
							}
						}

						if (pio == null) {
							pio = ioSource;
						}
						if (pio != null
								&& (pio.getPoisonous() > 0 || pio
										.getPoisonous_count() != 0)) {
							float rp = Player.getInstance().getPlayer()
									.getResist_poison();
							if (Diceroller.getInstance().ARXRnd() * 100.f > rp) {
								float oldPoison = Player.getInstance()
										.getPlayer().getPoison();
								Player.getInstance()
										.getPlayer()
										.setPoison(
												oldPoison + pio.getPoisonous());
							}

							if (pio.getPoisonous_count() != -1) {
								pio.setPoisonous_count((short) (pio
										.getPoisonous_count() - 1));
							}
						}
					}

					boolean alive;
					life = Player.getInstance().getPlayer().getLife();
					if (life > 0) {
						alive = true;
					} else {
						alive = false;
					}

					if (!Interface.getInstance().hasViewFlag(
							Interface.VW_CONTROLS_BLOCKED)) {
						life -= dmg;
						Player.getInstance().getPlayer().setLife(life);
					}

					if (life <= 0.f) {
						life = 0.f;
						Player.getInstance().getPlayer().setLife(life);

						if (alive) {
							REFUSE_GAME_RETURN = true;
							Player.getInstance().ARX_PLAYER_BecomesDead();
							boolean dieByFire = (type & DamageGlobals.DAMAGE_TYPE_FIRE) == DamageGlobals.DAMAGE_TYPE_FIRE;
							dieByFire = dieByFire
									|| (type & DamageGlobals.DAMAGE_TYPE_FAKEFIRE) == DamageGlobals.DAMAGE_TYPE_FAKEFIRE;
							if (dieByFire) {
								// ARX_SOUND_PlayInterface(SND_PLAYER_DEATH_BY_FIRE);
							}

							Script.getInstance().SendIOScriptEvent(
									Interactive.getInstance()
											.getInteractiveObject(0),
									ScriptConstants.SM_DIE, "", null);
							int nbmax = Interactive.getInstance().getNbmax();
							for (int i = 1; i < nbmax; i++) {
								InteractiveObject ioo = Interactive
										.getInstance().getInteractiveObject(i);

								if (ioo != null
										&& ioo.hasIoFlag(InteractiveGlobals.IO_NPC)
										&& (ioo.getTargetinfo() == 0 || ioo
												.getTargetinfo() == InteractiveGlobals.TARGET_PLAYER)) {
									Script
											.getInstance()
											.setEVENT_SENDER(
													Interactive
															.getInstance()
															.getInteractiveObject(
																	0));
									String killer;

									if (source == 0) {
										killer = "PLAYER";
									} else if (source <= -1) {
										killer = "NONE";
									} else if (Interactive.getInstance()
											.validIONum(source)
											&& Interactive
													.getInstance()
													.getInteractiveObject(
															source)
													.getFilename() != null
											&& Interactive
													.getInstance()
													.getInteractiveObject(
															source)
													.getFilename().charAt(0) != '0') {
										String temp = Interactive.getInstance()
												.getInteractiveObject(source)
												.getFilename();
										// sprintf(killer, "%s_%04d", temp,
										// inter.iobj[source]->ident);
									}

									Script
											.getInstance()
											.SendIOScriptEvent(
													Interactive
															.getInstance()
															.getInteractiveObject(
																	i), 0,
													killer, "TARGET_DEATH");
								}
							}
						}
					}
					float maxlife = Player.getInstance().getPlayer()
							.getMaxlife();
					if (maxlife <= 0.f) {
						val = damagesdone;
					}

					float t = dmg / maxlife;

					if (Blood_Pos == 0.f) {
						Blood_Pos = 0.000001f;
						Blood_Duration = (long) (100 + t * 200.f);
					} else {
						long temp = (long) (t * 800.f);
						Blood_Duration += temp;
					}
				}

				// revient les barres
				Interface.getInstance().ResetPlayerInterface();
			}
		}
		return val;
	}
	private void ARX_DAMAGES_DamagePlayerEquipment(final float damages) {
		float ratio = damages * GlobalConstants.DIV20;
		if (ratio > 1.f) {
			ratio = 1.f;
		}
		for (int i = 0; i < GlobalConstants.MAX_EQUIPED; i++) {
			short ref = Player.getInstance().getPlayer().getEquipped(i);
			if (ref != 0) {
				InteractiveObject todamage = Interactive.getInstance()
						.getInteractiveObject(ref);
				ARX_DAMAGES_DurabilityCheck(todamage, ratio);
			}
		}
	}
	public float ARX_DAMAGES_DrainMana(final InteractiveObject io,
			final float dmg) {
		float val = 0f;
		// only Players and NPCs can have their mana damaged
		if (io != null && io.hasIoFlag(InteractiveGlobals.IO_NPC)) {
			// if the IO is the Player
			if (Interactive.getInstance().isPlayer(io)) {
				boolean flag = Player.getInstance().getPlayer()
						.hasPlayerFlag(PlayerGlobals.PLAYERFLAGS_NO_MANA_DRAIN);
				// if the PC isn't shielded from mana drain
				if (!flag) {
					float mana = Player.getInstance().getPlayer().getMana();
					if (mana >= dmg) {
						Player.getInstance().getPlayer().setMana(mana - dmg);
						val = dmg;
					} else {
						val = mana;
						Player.getInstance().getPlayer().setMana(0);
					}
				}
			} else if (io.hasNpcData()) {
				if (io.getNpcdata().getMana() >= dmg) {
					io.getNpcdata().setMana(io.getNpcdata().getMana() - dmg);
					val = dmg;
				} else {
					val = io.getNpcdata().getMana();
					io.getNpcdata().setMana(0);
				}
			}
		}
		return val;
	}
	private void ARX_DAMAGES_DurabilityCheck(final InteractiveObject io,
			final float ratio) {
		if (io != null) {
			if (Diceroller.getInstance().ARXRnd() * 100f > io.getDurability()) {
				ARX_DAMAGES_DurabilityLoss(io, ratio);
			}
		}
	}
	private void ARX_DAMAGES_DurabilityLoss(final InteractiveObject io,
			final float loss) {
		if (io != null) {
			io.setDurability(io.getDurability() - loss);
			if (io.getDurability() <= 0) {
				Script.getInstance().SendIOScriptEvent(io,
						ScriptConstants.SM_BREAK, "", null);
			}
		}
	}
	public void ARX_DAMAGES_DurabilityRestore(final InteractiveObject io,
			final float percent) {
		if (io != null && io.getDurability() > 0
				&& io.getDurability() != io.getMaxDurability()) {
			if (percent >= 100.f) {
				io.setDurability(io.getMaxDurability());
			} else {
				float ratio = percent * GlobalConstants.DIV100;
				float toRestore = (io.getMaxDurability() - io.getDurability())
						* ratio;
				float v = Diceroller.getInstance().ARXRnd() * 100.f - percent;

				if (v <= 0.f) {
					float mloss = 1.f;

					if (io.hasIoFlag(InteractiveGlobals.IO_ITEM)
							&& io.hasItemData()) {
						float priceAdj = Common.getInstance()
								.checkLongValue(
										io.getItemData().getPrice()
												/ io.getMaxDurability());
						io.getItemData().setPrice(
								io.getItemData().getPrice() - (long) priceAdj);
					}
					io.setMaxDurability(io.getMaxDurability() - mloss);
				} else {
					if (v > 50.f) {
						v = 50.f;
					}

					v *= GlobalConstants.DIV100;
					float mloss = io.getMaxDurability() * v;

					if (io.hasIoFlag(InteractiveGlobals.IO_ITEM)
							&& io.hasItemData()) {
						io.getItemData()
								.setPrice(
										io.getItemData().getPrice()
												- (long) (io.getItemData()
														.getPrice() * v));
					}
					io.setMaxDurability(io.getMaxDurability() - mloss);
				}
				io.setDurability(io.getDurability() + toRestore);
				if (io.getDurability() > io.getMaxDurability()) {
					io.setDurability(io.getMaxDurability());
				}
				if (io.getMaxDurability() <= 0.f) {
					ARX_DAMAGES_DurabilityLoss(io, 100);
				}
			}
		}
	}
	public short ARX_DAMAGES_GetFree() {
		short free = (short) -1;
		for (short i = 0; i < DamageGlobals.MAX_DAMAGES; i++) {
			if (damages[i] == null) {
				damages[i] = new DamageInfo();
			}
			if (!damages[i].exists()) {
				damages[i].setRadius(100.f);
				damages[i].setStart_time(Time.getInstance().ARXTimeUL());
				damages[i].setDuration(1000);
				damages[i].setArea(0);
				damages[i].clearFlags();
				damages[i].setType(0);
				damages[i].setSpecial(0);
				damages[i].setSpecial_ID(0);
				damages[i].setLastupd(0);
				damages[i].setActive((short) 1);
				for (byte j = 0; j < 10; j++) {
					damages[i].setException(j, (short) -1);
				}
				free = i;
			}
		}
		return free;
	}
	public void ARX_DAMAGES_HealInter(final InteractiveObject io,
			final float dmg) {
		if (io != null && io.hasIoFlag(InteractiveGlobals.IO_NPC)
				&& io.getNpcdata().getLife() > 0) {
			// if the io is object 0, it is the player
			if (io.equals(Interactive.getInstance().getInteractiveObject(0))) {
				ARX_DAMAGES_HealPlayer(dmg);
			}
			if (dmg > 0.f) {
				io.getNpcdata().setLife(io.getNpcdata().getLife() + dmg);

				if (io.getNpcdata().getLife() > io.getNpcdata().getMaxlife()) {
					io.getNpcdata().setLife(io.getNpcdata().getMaxlife());
				}
			}
		}
	}
	public void ARX_DAMAGES_HealManaInter(final InteractiveObject io,
			final float dmg) {
		if (io != null && io.hasIoFlag(InteractiveGlobals.IO_NPC)) {
			// if the io is object 0, it is the player
			if (io.equals(Interactive.getInstance().getInteractiveObject(0))) {
				ARX_DAMAGES_HealManaPlayer(dmg);
			}
			if (io.getNpcdata().getLife() > 0) {
				if (dmg > 0.f) {
					io.getNpcdata().setMana(io.getNpcdata().getMana() + dmg);
					float mana = io.getNpcdata().getMana();
					float maxmana = io.getNpcdata().getMaxmana();
					if (mana > maxmana) {
						io.getNpcdata().setMana(maxmana);
					}
				}
			}
		}
	}
	public void ARX_DAMAGES_HealManaPlayer(final float dmg) {
		if (Player.getInstance().getPlayer().getLife() > 0f && dmg > 0.f) {
			float mana = Player.getInstance().getPlayer().getMana();
			Player.getInstance().getPlayer().setMana(mana + dmg);
			mana = Player.getInstance().getPlayer().getMana();
			float fullmaxmana = Player.getInstance().getPlayer()
					.getFull_maxmana();
			if (mana > fullmaxmana) {
				Player.getInstance().getPlayer().setMana(fullmaxmana);
			}
		}

	}
	public void ARX_DAMAGES_HealPlayer(final float dmg) {
		float life = Player.getInstance().getPlayer().getLife();
		if (life != 0 && dmg > 0.f) {
			boolean blocked = Interface.getInstance().hasViewFlag(
					Interface.VW_CONTROLS_BLOCKED);
			if (!blocked) {
				Player.getInstance().getPlayer().setLife(life + dmg);
			}

			life = Player.getInstance().getPlayer().getLife();
			float fullmaxlife = Player.getInstance().getPlayer()
					.getFull_maxlife();
			if (life > fullmaxlife) {
				Player.getInstance().getPlayer().setLife(fullmaxlife);
			}
		}
	}
	private void ARX_DAMAGES_IgnitIO(final InteractiveObject io, final float dmg) {
		if (io != null && !io.hasIoFlag(InteractiveGlobals.IO_INVULNERABILITY)) {

			if (io.getIgnition() <= 0.f && io.getIgnition() + dmg > 1.f) {
				Script.getInstance().SendIOScriptEvent(io,
						ScriptConstants.SM_ENTERZONE, "COOK_S", null);
			}

			if (io.hasIoFlag(InteractiveGlobals.IO_FIX)) {
				io.setIgnition(io.getIgnition() + dmg * GlobalConstants.DIV10);
			} else if (io.hasIoFlag(InteractiveGlobals.IO_ITEM)) {
				io.setIgnition(io.getIgnition() + dmg * GlobalConstants.DIV8);
			} else if (io.hasIoFlag(InteractiveGlobals.IO_NPC)) {
				io.setIgnition(io.getIgnition() + dmg * GlobalConstants.DIV4);
			}
		}
	}
	public void ARX_DAMAGES_Reset() {
		if (damages == null) {
			damages = new DamageInfo[DamageGlobals.MAX_DAMAGES];
		}
		for (int i = 0; i < damages.length; i++) {
			if (damages[i] == null) {
				damages[i] = new DamageInfo();
			}
			damages[i].setExists(false);
			damages[i].setRadius(100.f);
			damages[i].setStart_time(Time.getInstance().ARXTimeUL());
			damages[i].setDuration(1000);
			damages[i].setArea(0);
			damages[i].clearFlags();
			damages[i].setType(0);
			damages[i].setSpecial(0);
			damages[i].setSpecial_ID(0);
			damages[i].setLastupd(0);
			damages[i].setActive((short) 1);
			for (byte j = 0; j < 10; j++) {
				damages[i].setException(j, (short) -1);
			}
		}
	}
	public void ARX_DAMAGES_SCREEN_SPLATS_Init() {
		for (int i = 0; i < DamageGlobals.MAX_SCREEN_SPLATS; i++) {
			if (ssplat[i] == null) {
				ssplat[i] = new ScreenSplats();
			}
			ssplat[i].setExists(false);
		}
	}
	public boolean ARX_DAMAGES_TryToDoDamage(final Vector3 pos,
			final float dmg, final float radius, final int source) {
		boolean ret = false;
		int nbmax = Interactive.getInstance().getNbmax();
		for (int i = 0; i < nbmax; i++) {
			InteractiveObject io = Interactive.getInstance()
					.getInteractiveObject(i);

			if (io != null
					&& io.hasGameFlag(InteractiveGlobals.GFLAG_ISINTREATZONE)
					&& io.getShow() == GlobalConstants.SHOW_FLAG_IN_SCENE) {
				if (source != i) {
					float threshold;
					float rad = radius + 5.f;

					if (io.hasIoFlag(InteractiveGlobals.IO_FIX)) {
						threshold = 510;
						rad += 10.f;
					} else if (io.hasIoFlag(InteractiveGlobals.IO_NPC)) {
						threshold = 250;
					} else {
						threshold = 350;
					}

					if (EEDistance3D(pos, io.getPos()) < threshold) {
						if (SphereInIO(io, pos, rad)) {
							if (io.hasIoFlag(InteractiveGlobals.IO_NPC)) {
								if (Interactive.getInstance()
										.validIONum(source)) {
									Equipment
											.getInstance()
											.ARX_EQUIPMENT_ComputeDamages(
													Interactive
															.getInstance()
															.getInteractiveObject(
																	source),
													null, io, 1.f);
								}

								ret = true;
							}

							if (io.hasIoFlag(InteractiveGlobals.IO_FIX)) {
								ARX_DAMAGES_DamageFIX(io, dmg, source, 0, pos);
								ret = true;
							}
						}
					}
				}
			}
		}
		return ret;
	}
	private boolean InExceptList(final int dmg, final long num) {
		boolean is = false;
		for (int j = 0; j < 10; j++) {
			if (damages[dmg].getException(j) == num) {
				is = true;
			}
		}
		return is;
	}
}
